package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/util"
	"context"
	"database/sql"
	"fmt"
)

// EnsureAccountInfo 确保用户的账户信息并返回
func (s *UserServiceImp) EnsureAccountInfo(ctx context.Context, q *sqlc.Queries, withLock bool, userID int64) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	var sqlErr error
	if withLock {
		userAccount, sqlErr = q.GetUserAccountByUserIDToLock(ctx, userID)
	} else {
		userAccount, sqlErr = q.GetUserAccountByUserID(ctx, userID)
	}

	if sqlErr != nil {
		if sql.ErrNoRows == sqlErr {
			userAccount, err = q.UpsertUserAccount(ctx, sqlc.UpsertUserAccountParams{
				UserID:            userID,
				AvailableAmount:   0,
				PendingAmount:     0,
				WithdrawingAmount: 0,
				WithdrawedAmount:  0,
			})
			if err != nil {
				return
			}
		} else {
			err = sqlErr
			return
		}
	}
	slog.Infof("确保用户的账户信息并返回 ok")
	return
}

// UpdateAccountAvailableAmountForOrderBonusWithLockParam 更新用户总账可用金额(用户推广订单奖励金)参数
type UpdateAccountAvailableAmountForOrderBonusWithLockParam struct {
	UserAccountPtr        *sqlc.UserAccount
	OrderBonusAmountDelta int64 // OrderBonusAmountDelta: >0下单分账 <0分账回退 因为对于用户来说下单分账是收入,分账回退是支出
}

// UpdateAccountAvailableAmountForOrderBonusWithLock 更新用户总账可用金额(用户推广订单奖励金)
func (s *UserServiceImp) UpdateAccountAvailableAmountForOrderBonusWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountAvailableAmountForOrderBonusWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountAvailableAmount(ctx, sqlc.UpdateUserAccountAvailableAmountParams{
		UserID:          p.UserAccountPtr.UserID,
		AvailableAmount: p.UserAccountPtr.AvailableAmount + p.OrderBonusAmountDelta,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账可用金额(用户推广订单奖励金) ok")
	return
}

// UpdateAccountPendingAmountForOrderBonusWithLockParam 更新用户总账冻结金额(用户推广订单奖励金)参数
type UpdateAccountPendingAmountForOrderBonusWithLockParam struct {
	UserAccountPtr        *sqlc.UserAccount
	OrderBonusAmountDelta int64 // OrderBonusAmountDelta: >0下单分账 <0分账回退 因为对于用户来说下单分账是收入,分账回退是支出
}

// UpdateAccountPendingAmountForOrderBonusWithLock 更新用户总账冻结金额(用户推广订单奖励金)
func (s *UserServiceImp) UpdateAccountPendingAmountForOrderBonusWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountPendingAmountForOrderBonusWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountPendingAmount(ctx, sqlc.UpdateUserAccountPendingAmountParams{
		UserID:        p.UserAccountPtr.UserID,
		PendingAmount: p.UserAccountPtr.PendingAmount + p.OrderBonusAmountDelta,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账冻结金额(用户推广订单奖励金) ok")
	return
}

// UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLockParam 更新用户总账(用户推广订单奖励金)参数
type UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLockParam struct {
	UserAccountPtr               *sqlc.UserAccount
	AmountFromPendingToAvailable int64
}

// UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLock 更新用户总账(用户推广订单奖励金从冻结到可用)
func (s *UserServiceImp) UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountSubtractPendingAmountToAddAvailableAmount(ctx, sqlc.UpdateUserAccountSubtractPendingAmountToAddAvailableAmountParams{
		UserID:        p.UserAccountPtr.UserID,
		PendingAmount: p.AmountFromPendingToAvailable,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账(用户推广订单奖励金) ok")
	return
}

// UpdateAccountForUserTransferRequestWithLockParam 更新用户总账(用户转账(提现)申请)参数
type UpdateAccountForUserTransferRequestWithLockParam struct {
	UserAccountPtr *sqlc.UserAccount
	WithdrawAmount int64 // >0
}

// UpdateAccountForUserTransferRequestWithLock 更新用户总账(用户转账(提现)申请)
func (s *UserServiceImp) UpdateAccountForUserTransferRequestWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForUserTransferRequestWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountSubtractAvailableAmountToAddWithdrawingAmount(ctx, sqlc.UpdateUserAccountSubtractAvailableAmountToAddWithdrawingAmountParams{
		UserID:          p.UserAccountPtr.UserID,
		AvailableAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账(用户转账(提现)申请) ok")
	return
}

// UpdateAccountForUserTransferSuccessWithLockParam 更新用户总账(用户转账(提现)成功)参数
type UpdateAccountForUserTransferSuccessWithLockParam struct {
	UserAccountPtr *sqlc.UserAccount
	WithdrawAmount int64 // >0
}

// UpdateAccountForUserTransferSuccessWithLock 更新用户总账(用户转账(提现)成功)
func (s *UserServiceImp) UpdateAccountForUserTransferSuccessWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForUserTransferSuccessWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountSubtractWithdrawingAmountToAddWithdrawedAmount(ctx, sqlc.UpdateUserAccountSubtractWithdrawingAmountToAddWithdrawedAmountParams{
		UserID:            p.UserAccountPtr.UserID,
		WithdrawingAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账(用户转账(提现)成功) ok")
	return
}

// UpdateAccountForUserTransferFailWithLockParam 更新用户总账(用户转账(提现)失败)参数
type UpdateAccountForUserTransferFailWithLockParam struct {
	UserAccountPtr *sqlc.UserAccount
	WithdrawAmount int64 // >0
}

// UpdateAccountForUserTransferFailWithLock 更新用户总账(用户转账(提现)失败)
func (s *UserServiceImp) UpdateAccountForUserTransferFailWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForUserTransferFailWithLockParam) (userAccount sqlc.UserAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userAccount, err = q.UpdateUserAccountSubtractWithdrawingAmountToAddAvailableAmount(ctx, sqlc.UpdateUserAccountSubtractWithdrawingAmountToAddAvailableAmountParams{
		UserID:            p.UserAccountPtr.UserID,
		WithdrawingAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}

	slog.Infof("更新用户总账(用户转账(提现)失败) ok")
	return
}


type BatchUpdateAccountWithdrawApplymentApplyStateParam struct {
	IDs        []int64
	ApplyState string
}

// BatchUpdateAccountWithdrawApplymentApplyState 批量更新用户提现状态
func (s *UserServiceImp) BatchUpdateAccountWithdrawApplymentApplyState(ctx context.Context, q *sqlc.Queries, p BatchUpdateAccountWithdrawApplymentApplyStateParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.BatchUpdateUserAccountWithdrawApplymentApplyState(ctx, sqlc.BatchUpdateUserAccountWithdrawApplymentApplyStateParams{
		Column1:    p.IDs,
		ApplyState: p.ApplyState,
	})

	slog.Infof("批量更新用户提现状态=>%s成功", p.ApplyState)
	return
}

// SubmitAccountWithdrawApplymentParam 提交用户提现申请参数
type SubmitAccountWithdrawApplymentParam struct {
	UserID   int64
	Target   sqlc.EmWithdrawTarget
	Amount   int64
	Remark   string
	RealName string
	IdCardNo string
}

// SubmitAccountWithdrawApplyment 提交用户提现申请
func (s *UserServiceImp) SubmitAccountWithdrawApplyment(ctx context.Context, q *sqlc.Queries, p SubmitAccountWithdrawApplymentParam) (userAccountWithdrawApplyment sqlc.UserAccountWithdrawApplyment, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userID := p.UserID
	isWithdrawToWechatChangePurse := p.Target == sqlc.EmWithdrawTargetWechatChangePurse

	if !isWithdrawToWechatChangePurse {
		err = fmt.Errorf("提现到银行卡暂时没实现")
		return
	}

	user, err := q.GetUser(ctx, userID)
	if err != nil {
		return
	}

	// 如果用户的实名和身份证不存在,则从入参取,如果存在忽略入参
	realName := user.RealName.String
	idCardNo := user.IDCardNo.String
	if user.RealName.String != p.RealName {
		if p.RealName == "" {
			err = fmt.Errorf("用户提现必须填写实名")
			return
		}
		user, err = q.UpdateUserRealName(ctx, sqlc.UpdateUserRealNameParams{
			ID:       user.ID,
			RealName: sqlc.NSFromString(p.RealName),
		})
		if err != nil {
			return
		}
		realName = user.RealName.String
	}
	if user.IDCardNo.String != p.RealName {
		if p.IdCardNo == "" {
			err = fmt.Errorf("用户提现必须填写身份证")
			return
		}
		user, err = q.UpdateUserIDCardNo(ctx, sqlc.UpdateUserIDCardNoParams{
			ID:       user.ID,
			IDCardNo: sqlc.NSFromString(p.IdCardNo),
		})
		if err != nil {
			return
		}
		idCardNo = user.IDCardNo.String
	}

	userAccountWithdrawApplyment, err = q.CreateUserAccountWithdrawApplyment(ctx, sqlc.CreateUserAccountWithdrawApplymentParams{
		UserID:     userID,
		Target:     string(p.Target),
		AppID:      s.wechatVars.MinaAppID, //s.vars.AppID,
		MchID:      s.vars.MchIDAsProfit,
		Amount:     p.Amount,
		Remark:     p.Remark,
		OpenID:     user.OpenID,
		UserName:   realName,
		UserIDCard: sqlc.NSFromString(idCardNo),
	})
	if err != nil {
		return
	}
	slog.Infof("2 创建用户提现申请 ok")

	outDetailNo, err := s.transferApply(ctx, &userAccountWithdrawApplyment)
	if err != nil {
		return
	}
	slog.Infof("3 提交转账申请[outDetailNo:%s] ok", outDetailNo)

	slog.Infof("userAccountWithdrawApplyment=>%v", userAccountWithdrawApplyment)

	userAccountWithdrawApplyment, err = q.UpdateUserAccountWithdrawApplymentOutDetailNo(ctx, sqlc.UpdateUserAccountWithdrawApplymentOutDetailNoParams{
		ID:          userAccountWithdrawApplyment.ID,
		OutDetailNo: sqlc.NSFromString(outDetailNo),
	})
	if err != nil {
		slog.Errorf("UpdateUserAccountWithdrawApplymentOutDetailNo err:%v", err)
		return
	}
	slog.Infof("4 更新用户提现申请的转账详情单号 ok")

	userAccount, err := s.EnsureAccountInfo(ctx, q, true, userID)
	if err != nil {
		return
	}
	slog.Infof("5 确保用户账户记录 ok")

	userAccount, err = s.UpdateAccountForUserTransferRequestWithLock(ctx, q, UpdateAccountForUserTransferRequestWithLockParam{
		UserAccountPtr: &userAccount,
		WithdrawAmount: p.Amount,
	})
	if err != nil {
		return
	}
	slog.Infof("5 用户总账记账=>提现申请 ok")

	return
}

// modifySettlementAccount wxpay 提交转账申请
func (s *UserServiceImp) transferApply(ctx context.Context, userAccountWithdrawApplymentPtr *sqlc.UserAccountWithdrawApplyment) (outDetailNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	bizNo := fmt.Sprintf("USR:%d", userAccountWithdrawApplymentPtr.ID)
	req := &wxpay.TransferApplyRequest{
		AppId:      userAccountWithdrawApplymentPtr.AppID,
		MchId:      userAccountWithdrawApplymentPtr.MchID,
		Amount:     userAccountWithdrawApplymentPtr.Amount,
		Remark:     userAccountWithdrawApplymentPtr.Remark,
		OpenId:     userAccountWithdrawApplymentPtr.OpenID,
		UserName:   userAccountWithdrawApplymentPtr.UserName,
		UserIdCard: userAccountWithdrawApplymentPtr.UserIDCard.String,
		BizNo:      bizNo,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.WxpayCl.TransferApply(ctxWithTimout, req)
	if err != nil {
		return
	}
	outDetailNo = reply.OutDetailNo
	return
}

// GetAccountInfo 获取用户的账户信息
func (s *UserServiceImp) GetAccountInfo(ctx context.Context, q *sqlc.Queries, userID int64) (userAccount sqlc.UserAccount, err error) {
	return s.EnsureAccountInfo(ctx, q, false, userID)
}
